package com.android.remote;

import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Process;

import com.android.incallui.Log;
import com.android.remote.bean.RemoteBean;
import com.android.remote.bean.RemoteReceiveBean;
import com.android.remote.call.RemoteCall;
import com.android.remote.call.RemoteCallback;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

/**
 * 远程异步调用
 */
public class RemoteAsyncCall {
    /**
     * 远程调用池
     */
    private static final Map<String, RemoteReceiveBean> REMOTE_CALL_POOL = new HashMap<>();
    /**
     * 任务队列
     */
    private static final Map<String, RemoteCall> TASK_QUEUE = new HashMap<>();
    /**
     * 远程调用广播过滤
     */
    private static final String REMOTE_SYNC_CALL = "com.ifengke.remote.synchronize.call";
    /**
     * 远程回调广播过滤
     */
    private static final String REMOTE_SYNC_CALLBACK = "com.ifengke.remote.synchronize.callback";
    /**
     * 功能字段
     */
    private static final String ACTION = "remoteAction";
    /**
     * uuid
     */
    private static final String UUID_EXTRA = "uuid";
    /**
     * 上下文
     */
    @SuppressLint("StaticFieldLeak")
    private static Context context;

    /**
     * key:当前进程id
     * value:当前进程中 receiver 的地址值
     * 保证一个广播不会被多个进程同时收到
     */
    private static ConcurrentHashMap<Integer, String> cacheProcess;
    private static BroadcastReceiver receiver = new BroadcastReceiver() {

        @Override
        public synchronized void onReceive(final Context context, Intent intent) {
            if (!cacheProcess.containsKey(Process.myPid())) {
                return;
            }
            if (!this.toString().equals(cacheProcess.get(Process.myPid()))) {
                //地址值不匹配
                return;
            }
            final Bundle bundle = intent.getExtras();
            if (bundle != null) {
                //根据动作取数据对象
                final RemoteReceiveBean bean = REMOTE_CALL_POOL.get(bundle.getString(ACTION));
                if (bean != null) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            //存在对象则调用该数据对象的receive方法
                            try {
                                bean.receive(bundle);
                            } catch (Throwable throwable) {
                                Log.e("AD-HYL", "onReceive:" + throwable);
                            }
                            //发送一个回调
                            context.sendBroadcast(new Intent(REMOTE_SYNC_CALLBACK)
                                    .putExtras(bundle));
                        }
                    }).start();
                }
            }
        }
    };

    /**
     * 初始化，不论是发送还是接受都需要调用
     *
     * @param context 上下文
     */
    public static void init(Context context) {
        if (context != null) {
            Context appContext = context.getApplicationContext();
            if (appContext != null) {
                context = appContext;
            }
        }
        RemoteAsyncCall.context = context;

    }

    /**
     * 注册远程调用接收器，需要响应远程调用的进程调用该方法
     *
     * @param beans 远程调用对象
     */
    @SafeVarargs
    public static void registerRemoteCall(Class<? extends RemoteReceiveBean>... beans) throws IllegalAccessException, InstantiationException {
        //防止多次注册
        if (cacheProcess == null) {
            cacheProcess = new ConcurrentHashMap<>(16);
        }
        if (cacheProcess.containsKey(Process.myPid())) {
            return;
        }
        cacheProcess.put(Process.myPid(), receiver.toString());
        registerRemoteBean(beans);
        //远程调用响应广播
        context.registerReceiver(receiver, new IntentFilter(REMOTE_SYNC_CALL));
    }

    /**
     * 注册远程调用回调
     * 需要回调响应的进程调用该方法
     */
    public static synchronized void registerRemoteCallback() {
        /*
        远程回调响应广播
         */
        context.registerReceiver(new BroadcastReceiver() {
            @Override
            public synchronized void onReceive(Context context, Intent intent) {
                final Bundle bundle = intent.getExtras();
                if (bundle != null) {
                    /*
                    取广播唯一标识
                     */
                    String uuid = bundle.getString(UUID_EXTRA);
                    /*
                    在任务队列装寻找
                     */
                    if (TASK_QUEUE.containsKey(uuid)) {
                        RemoteCall remoteCall = TASK_QUEUE.remove(uuid);
                        /*
                        执行回调
                         */
                        if (remoteCall != null) {
                            remoteCall.setBundle(bundle);
                            remoteCall.call();
                        }
                    }
                }
            }
        }, new IntentFilter(REMOTE_SYNC_CALLBACK));
    }

    /**
     * 远程调用
     *
     * @param bean 数据
     */
    public static synchronized void remoteCall(RemoteBean bean) {
        remoteCall(bean, null);
    }

    /**
     * 远程调用
     *
     * @param bean 数据
     * @param call 回调
     */
    public static synchronized void remoteCall(RemoteBean bean, RemoteCall call) {
        if (call == null) {
            remoteCall(bean, null, null);
        } else {
            if (call instanceof RemoteCallback) {
                remoteCall(bean, null, (RemoteCallback) call);
            } else {
                remoteCall(bean, call, null);
            }
        }
    }

    /**
     * 远程调用
     *
     * @param bean     数据
     * @param call     回调操作，远程任务完成自动响应
     * @param callback 回调操作，需要手动响应回调
     */
    @SuppressLint("NewApi")
    public static synchronized void remoteCall(RemoteBean bean, RemoteCall call, RemoteCallback callback) {
        final String uuid = UUID.randomUUID().toString();
        /*
        取功能名称
         */
        String action = bean.action();
        /*
        放入这次的远程调用
         */
        if (call != null) {
            TASK_QUEUE.put(uuid, call);
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    /*
                    指定时长过后如果该回调id还存在任务队列中时则表示超时
                     */
                    RemoteCall remoteCall = TASK_QUEUE.get(uuid);
                    Optional.ofNullable(remoteCall)
                            .ifPresent(new Consumer<RemoteCall>() {
                                @Override
                                public void accept(RemoteCall timeoutRemoteCall) {
                                    /*
                                    移除任务id
                                     */
                                    TASK_QUEUE.remove(uuid);
                                    /*
                                    执行超时回调逻辑
                                     */
                                    timeoutRemoteCall.timeout();
                                }
                            });
                }
            }, call.timeoutLen() * 1000);
        }
        /*
        放入这次的远程回调
         */
        if (callback != null) {
            TASK_QUEUE.put(callback.action, callback);
        }
        /*
        发送
         */
        context.sendBroadcast(bean
                .intentFormat()
                .setAction(REMOTE_SYNC_CALL)
                .putExtra(ACTION, action)
                .putExtra(UUID_EXTRA, uuid)
        );
    }

    /**
     * 手动远程回调，需要在调用时指定回调标识
     *
     * @param action 回调标识
     */
    public static synchronized void remoteCallback(String action) {
        /*
        发送
         */
        context.sendBroadcast(new Intent()
                .setAction(REMOTE_SYNC_CALLBACK)
                .putExtra(UUID_EXTRA, action)
        );
    }

    /**
     * 注册远程对象
     *
     * @param beans 远程对象
     */
    @SafeVarargs
    private static void registerRemoteBean(Class<? extends RemoteReceiveBean>... beans) throws InstantiationException, IllegalAccessException {
        for (Class<? extends RemoteReceiveBean> beanClass : beans) {
            RemoteReceiveBean bean = beanClass.newInstance();
            REMOTE_CALL_POOL.put(bean.action1(), bean);
        }
    }
}
